# This file is part of the AutoMAT distribution (https://bitbucket.com/mahomaho/AutoMAT).
# Copyright (c) 2021 Mattias Holmqvist.
# 
# AutoMAT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# AutoMAT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with AutoMAT.  If not, see <https://www.gnu.org/licenses/>.

import lxml.etree as ET
import re
import itertools
import sys
import keyword
#import math
import io

schemafile='AutoMAT/AUTOSAR_00049/AUTOSAR_00049.xsd'
schemafile='AutoMAT/AUTOSAR_00046/AUTOSAR_00046.xsd'
schemafile='AutoMAT/AUTOSAR_4-2-2/AUTOSAR_4-2-2.xsd'
schemafile='AutoMAT/AUTOSAR_00052/AUTOSAR_00052.xsd'

tree = ET.parse(schemafile)

class iostream:
	def __init__(self,file=sys.stdout):
		self._file=file
	def __lshift__(self,other):
		print(other,end='',file=self._file)
		if self._file is not sys.stdout: 
			print(other,end='')
		return self

header='''
# This file is part of the AutoMAT distribution (https://bitbucket.com/mahomaho/AutoMAT).
# Copyright (c) 2021 Mattias Holmqvist.
# 
# AutoMAT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# AutoMAT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with AutoMAT.  If not, see <https://www.gnu.org/licenses/>.

'''
simpleprinter=iostream(open(schemafile.rsplit('/',1)[0]+'/autosar_r4p0/SimpleTypes.py',mode='wt',encoding='utf-8'))
simpleprinter << header
groupprinter=iostream(open(schemafile.rsplit('/',1)[0]+'/autosar_r4p0/Group.py',mode='wt',encoding='utf-8'))
groupprinter << header
printer=iostream(open(schemafile.rsplit('/',1)[0]+'/autosar_r4p0/__init__.py',mode='wt',encoding='utf-8'))
printer << header
groupinit=iostream(io.StringIO())

class Annotation:
	dummyAnnotation=False
	def __init__(self,element):
		annotation=element.find('{http://www.w3.org/2001/XMLSchema}annotation')
		doc=annotation.find('{http://www.w3.org/2001/XMLSchema}documentation')
		self.doc='"""'+doc.text.replace('"', r'\"')+'"""' if doc is not None else '""'
		self.stereotypes=[]
		self.maxOccurs=1
		self.minOccurs=0
		self.sequenceOffset=0
		self.Splitkey=[]
		self.isOrdered = False
		#self.doc
		for appinfo in annotation.iterfind('{http://www.w3.org/2001/XMLSchema}appinfo'):
			if appinfo.attrib['source']=="tags":
				for param,val in zip((' '+appinfo.text).split('"')[0::2],appinfo.text.split('"')[1::2]):
					#for tag in appinfo.text.split(';'):
					#param,val=tag.strip().split('=')
					param=param[1:-1]
					if param=='mmt.qualifiedName':
						self.name=val.strip()
						if '.' in self.name:
							containername,elementname=self.name.split('.')
							containername=containername.replace('/','_')
							if containername in keyword.kwlist:
								containername+='_'
							self.containername=containername
						else:
							elementname=self.name
						elementname=elementname.replace('/','_')
						if elementname in keyword.kwlist:
							elementname+='_'
						self.elementname=elementname
					elif param=='atp.Splitkey':
						self.Splitkey=val.split(', ')
					elif param=='pureMM.isOrdered':
						self.isOrdered=val.strip('" ').lower()=='true'
					elif param=='pureMM.maxOccurs':
						try:
							self.maxOccurs=int(val.strip('" '))
							if self.maxOccurs < 0:
								self.maxOccurs=4294967295
						except:
							pass
					elif param=='pureMM.minOccurs':
						try:
							self.minOccurs=int(val.strip('" '))
						except:
							pass
					elif param=='xml.sequenceOffset':
						try:
							self.sequenceOffset=int(val.strip('" '))
						except:
							self.sequenceOffset=1000000
			elif appinfo.attrib['source']=="stereotypes":
				self.stereotypes=appinfo.text.split(',')
		for idx,key in enumerate(self.Splitkey):
			if key.startswith(self.elementname+'.'):
				key=key[len(self.elementname)+1:]
			self.Splitkey[idx]=key
		self.Splitkey=tuple(self.Splitkey)

unnamedcounter=0

class Element:
	elements=[]
	def __init__(self,element,parent):
		self.parent=parent
		try:
			self.annotation=Annotation(element)
		except:
			name=element.attrib['name'].lower().split('-')
			name[1:]=(n[0].upper()+n[1:] for n in name[1:])
			name=''.join(name)
			class dummyannotation:
				maxOccurs=1
				minOccurs=0
				stereotypes=[]
				Splitkey=()
				isOrdered=False
				doc='"""Undocumented element"""'
				sequenceOffset=0
			self.annotation=dummyannotation()
			self.annotation.name=name
			self.xml=element
		Element.elements.append(self)				
			#element.
		self.xmlname=element.attrib['name']
		if 'type' in element.attrib:
			self.xmltypename=element.attrib['type']
		else:
			complexType=element.find('{http://www.w3.org/2001/XMLSchema}complexType')
			if complexType is not None:
				self.type=ComplexType(complexType)
				if self.annotation.__class__.__name__=='dummyannotation' and len(self.type.elements)==1 and hasattr(self.type.elements[0],'annotation'):
					self.annotation=self.type.elements[0].annotation
			else:
				sdjfhksjdh
				simpleType=element.find('{http://www.w3.org/2001/XMLSchema}simpleType')
				if complexType is not None:
					self.type=ComplexType(complexType)
				else:
					dsjdh
	def gettype(self):
		if hasattr(self,'xmltypename'):
			if self.xmltypename in ComplexType.types:
				return ComplexType.types[self.xmltypename]
			else:
				class dummycomplex:
					typeUsed=True
					simple=True
					mixed=False
					elements=[]
					attrib=[]
					attribGroups=[]
					baseclasses=[]
					simpletype=self.xmltypename
					generate=ComplexType.generate
					getelements=ComplexType.getelements
					getattribs=ComplexType.getattribs
					getattriblist=ComplexType.getattriblist
					def getaggregationlist(self):
						return []
					def getbaseclasses(self):
						return self.baseclasses
					#def getattribs(self):
					#	return []
				self.type=dummycomplex()
				del self.xmltypename
				return self.gettype()
		elif hasattr(self,'type') and self.type.simple:
			global unnamedcounter
			self.type.xmlname='UNNAMED-ELEMENT-TYPE-NR'+str(unnamedcounter)
			class dummy:
				doc='""'
				name='_unnamedElementTypeNr'+str(unnamedcounter)
				dummyAnnotation=True
				stereotypes=[]
			self.type.annotation=dummy()
			unnamedcounter+=1
			self.xmltypename='AR:'+self.type.xmlname
			if self.type.simpletype not in SimpleType.types:
				basetype=ComplexType.types[self.type.simpletype]
				self.type.annotation.doc=basetype.annotation.doc
				self.type.annotation.stereotypes=basetype.annotation.stereotypes
				self.type.annotation.Splitkey=basetype.annotation.Splitkey
				self.type.simpletype=basetype.simpletype
				self.type.attrib.extend(basetype.attrib)
				self.type.attribGroups.extend(basetype.attribGroups)
				self.type.baseclasses.extend(basetype.baseclasses)
			ComplexType.types[self.xmltypename]=self.type
			return self.type
		else:
			return self.type
	def get_parent_name(self):
		try:
			typen,name=self.annotation.name.split('.')
		except:
			# fix for some elements without specified base
			typen=self.parent.annotation.name
			name=self.annotation.name
		if name in keyword.kwlist:
			name+='_'
		if typen!=self.parent.annotation.name and len(list(g for g in Group.types.values() if g.annotation.name==typen))!=1:
			typen=self.parent.annotation.name
		return typen,name
	def generate_groupinit(self):
		etype=self.gettype()
		try:
			typen,name=self.get_parent_name()
		except:
			return
		initpriter=Group.classtogroup[typen].initstr
		if self.annotation.maxOccurs==1:
			if etype.simple:
				initpriter << f'''
		self._{name}_child=ModelNone'''
			elif not hasattr(self,'type'):
				initpriter << f'''
		self._{name}_child=ModelNone'''
			else:
				initpriter << f'''
		self._{name}_child=[]'''
		elif etype.simple or hasattr(self,'type') and etype.getelements()[0].gettype().simple:
			initpriter << f'''
		self._{name}_children=[]'''
		else:
			initpriter << f'''
		self._{name}_children=[]'''
		
	def generate(self,printer,parent):
		try:
			typen,name=self.annotation.name.split('.')
		except:
			# fix for some elements without specified base
			typen=parent.annotation.name
			name=self.annotation.name
		if name in keyword.kwlist:
			name+='_'
		if typen!=parent.annotation.name and len(list(g for g in Group.types.values() if g.annotation.name==typen))!=1:
			typen=parent.annotation.name
		etype=self.gettype()
		etype.typeUsed=True
		self.ingroup=typen
		for element in etype.getelements():
			element.gettype().typeUsed=True
		#if typen=='ClientServerOperation' and name=='possibleError':
		#	a=4
		if not hasattr(self.annotation, 'SplitKeyFixed'):
			self.annotation.SplitKeyFixed = True
			"""
			if name in self.annotation.Splitkey:
				idx=self.annotation.Splitkey.index(name)
				if self.gettype().simple:
					newkey = list(self.annotation.Splitkey)
					newkey[idx]=''
				else:
					if name == 'referenceValue':
						print('apa')
					newkey = list(self.annotation.Splitkey[:idx])
					def getsimple(elements):
						ret = list()
						for element in elements:
							if 'atpSplitable' not in element.annotation.stereotypes and element.gettype().simple:# and element.annotation.maxOccurs==1:
								try:
									subtypen,subname=element.annotation.name.split('.')
								except:
									subname=element.annotation.name
								ret.append(subname)
						return ret
					if hasattr(self.gettype(), 'annotation'):
						newkey.extend(getsimple(self.gettype().getelements()))
					else:
						elements = self.gettype().getelements()
						a = getsimple(elements[0].gettype().getelements())
						for element in elements[1:]:
							b= getsimple(element.gettype().getelements())
							pos=0
							for B in b:
								if B in a:
									pos = a.index(B)+1
								else:
									a.insert(pos, B)
									pos+=1
						if not a:
							a.append('')
						newkey.extend(a)
					newkey.extend(self.annotation.Splitkey[idx+1:])
				self.annotation.Splitkey = tuple(newkey)"""
			newkey = []
			for key in self.annotation.Splitkey:
				if key == name:

				
					if self.gettype().simple:
						#newkey = list(self.annotation.Splitkey)
						newkey.append('.')
						continue
					def getsimple(elements):
						ret = list()
						for element in elements:
							if 'atpSplitable' not in element.annotation.stereotypes and element.gettype().simple:# and element.annotation.maxOccurs==1:
								try:
									subtypen,subname=element.annotation.name.split('.')
								except:
									subname=element.annotation.name
								ret.append(element.xmlname)
						return ret
					if hasattr(self.gettype(), 'annotation'):
						newkey.extend(getsimple(self.gettype().getelements()))
					else:
						elements = self.gettype().getelements()
						if len(elements) == 1 and elements[0].gettype().simple:
							newkey.append('.')
						else:
							a = getsimple(elements[0].gettype().getelements())
							for element in elements[1:]:
								b= getsimple(element.gettype().getelements())
								pos=len(newkey)
								for B in b:
									if B in a:
										pos = a.index(B)+1
									else:
										a.insert(pos, B)
										pos+=1
							if not a:
								pass
								#a.append('.')
							newkey.extend(a)
					continue
				
				try:
					if key == '':
						newkey.append('.')
						continue
					if hasattr(self.gettype(), 'annotation'):
						elements = [self]
					else:
						elements = self.gettype().getelements()
					xmlpath = []
					for subkey in key.split('.'):
						for element in elements:
							for subelement in element.gettype().getelements():
								try:
									subtypen,subname=subelement.annotation.name.split('.')
								except:
									subname=subelement.annotation.name
								if subname.lower() == subkey.lower():
									xmlpath.append(subelement.xmlname)
									elements = [subelement]
									break
							else:
								continue
							break
						else:
							assert False, 'cannot find splitkey'
					newkey.append('/'.join(xmlpath))
				except AssertionError as e:
					print(f'Warning: splitkey {key}')
			self.annotation.Splitkey = tuple(newkey)
		if self.annotation.maxOccurs==1:
			if etype.simple:
				printer << f'''
	Group.{typen}.{name}=complexbase.Element(Group.{typen},'{name}',True,{self.annotation.minOccurs},{'atpSplitable' in self.annotation.stereotypes},{self.annotation.Splitkey},"{self.xmlname}",{self.gettype().annotation.name},"_{name}_child",{self.annotation.doc})'''
			elif not hasattr(self,'type'):
				printer << f'''
	Group.{typen}.{name}=complexbase.Element(Group.{typen},'{name}',{etype.mixed},{self.annotation.minOccurs},{'atpSplitable' in self.annotation.stereotypes},{self.annotation.Splitkey},"{self.xmlname}",{self.gettype().annotation.name},"_{name}_child",{self.annotation.doc})'''
			else:
				printer << f'''
	Group.{typen}.{name}=complexbase.Elements(Group.{typen},'{name}',{etype.mixed},{self.annotation.minOccurs},{'atpSplitable' in self.annotation.stereotypes},{self.annotation.Splitkey},False,{self.annotation.maxOccurs},"{self.xmlname}",{{{','.join('"'+element.xmlname+'":'+element.gettype().annotation.name for element in self.gettype().getelements())}}},"_{name}_child",{self.annotation.doc})'''
		elif etype.simple or hasattr(self,'type') and not [e for e in etype.getelements() if not e.gettype().simple]:
			if etype.simple:
				printer << f'''
	Group.{typen}.{name}=complexbase.Elements(Group.{typen},'{name}',True,{self.annotation.minOccurs},{'atpSplitable' in self.annotation.stereotypes},{self.annotation.Splitkey},{self.annotation.isOrdered},{self.annotation.maxOccurs},"{self.xmlname}",{self.gettype().annotation.name},"_{name}_children",{self.annotation.doc})'''
			elif hasattr(self,'type'):
				printer << f'''
	Group.{typen}.{name}=complexbase.Elements(Group.{typen},'{name}',True,{self.annotation.minOccurs},{'atpSplitable' in self.annotation.stereotypes},{self.annotation.Splitkey},{self.annotation.isOrdered},{self.annotation.maxOccurs},"{self.xmlname}",{{{','.join('"'+element.xmlname+'":'+element.gettype().annotation.name for element in etype.getelements())}}},"_{name}_children",{self.annotation.doc})'''
			else:
				printer << f'''
	Group.{typen}.{name}=complexbase.Elements(Group.{typen},'{name}',True,{self.annotation.minOccurs},{'atpSplitable' in self.annotation.stereotypes},{self.annotation.Splitkey},{self.annotation.isOrdered},{self.annotation.maxOccurs},"{self.xmlname}",{self.gettype().annotation.name},"_{name}_children",{self.annotation.doc})'''
		else:
			if hasattr(self,'type'):
				printer << f'''
	Group.{typen}.{name}=complexbase.Elements(Group.{typen},'{name}',{etype.mixed},{self.annotation.minOccurs},{'atpSplitable' in self.annotation.stereotypes},{self.annotation.Splitkey},{self.annotation.isOrdered},{self.annotation.maxOccurs},"{self.xmlname}",{{{','.join('"'+element.xmlname+'":'+element.gettype().annotation.name for element in etype.getelements())}}},"_{name}_children",{self.annotation.doc})'''
			else:
				printer << f'''
	Group.{typen}.{name}=complexbase.Elements(Group.{typen},'{name}',{etype.mixed},{self.annotation.minOccurs},{'atpSplitable' in self.annotation.stereotypes},{self.annotation.Splitkey},{self.annotation.isOrdered},{self.annotation.maxOccurs},"{self.xmlname}",{etype.annotation.name},"_{name}_children",{self.annotation.doc})'''
		self.childref=f'"{self.xmlname}":Group.{typen}.{name}'
		Group.classtogroup[typen].aggregationslist.append((name,self))
# 		if 'isOfType' in self.annotation.stereotypes:
# 			printer << f'''
# 	{typen}._isOfType={typen}.{name}'''
		

class Attrib:
	def __init__(self,attrib):
		try:
			self.annotation=Annotation(attrib)
		except:
			class dummyAttribAnnotation:
				name="dummyAttrib."+attrib.attrib['name'].replace('-','_')
				elementname=attrib.attrib['name'].replace('-','_')
				doc='""'
				dummyAnnotation=True
			self.annotation=dummyAttribAnnotation()
		try:
			self.xmlname=attrib.attrib['name']
			self.xmltype=attrib.attrib['type']
		except:
			if attrib.attrib['ref']=='xml:space':
				self.xmlname='space'
				self.xmltype='AR:STRING--SIMPLE'
			else:
				raise
		self.required=attrib.attrib.get('use','')=='required'

class AttributeGroup:
	types={}
	ingroup=None
	def __init__(self,attribg):
		try:
			self.annotation=Annotation(attribg)
		except:pass
		self.xmlname=attribg.attrib['name']
		self.types['AR:'+self.xmlname]=self
		self.attribute=[]
		for attr in attribg.iterfind('{http://www.w3.org/2001/XMLSchema}attribute'):
			self.attribute.append(Attrib(attr))
		
		
class SimpleType:
	types={}
	def __init__(self,simple):
		try:
			self.annotation=Annotation(simple)
		except:
			class dummyAnnotation:
				dummyAnnotation=True
				doc='""'
				name=simple.attrib['name'].replace('-','_')
			self.annotation=dummyAnnotation()
		self.xmlname=simple.attrib['name']
		self.types['AR:'+self.xmlname]=self
		restriction=simple.find('{http://www.w3.org/2001/XMLSchema}restriction')
		self._base=restriction.attrib['base']
		pattern=restriction.find('{http://www.w3.org/2001/XMLSchema}pattern')
		if pattern is not None:
			self.pattern=pattern.attrib['value']
		self.enumeration=[]
		for enumeration in restriction.iterfind('{http://www.w3.org/2001/XMLSchema}enumeration'):
			value=enumeration.attrib['value']
			try:
				annotation=Annotation(enumeration)
			except:
				class dummyEnumAnnotation:
					doc='""'
					dummyAnnotation=True
				annotation=dummyEnumAnnotation()
			if not hasattr(annotation,'name'):
				annotation.name="dummyAttrib."+enumeration.attrib['value'].replace('-','_')
				annotation.dummyAnnotation=True
			if not hasattr(annotation,'elementname'):
				annotation.elementname=enumeration.attrib['value'].replace('-','_')
				annotation.dummyAnnotation=True
			self.enumeration.append((value,annotation))
	def generate(self):
		if len(self.enumeration)>0 and self.enumeration[0][1] is not None:
			simpleprinter << f'''

class {self.annotation.name}(simplebase.Enum):
	{self.annotation.doc}'''
			for val,an in self.enumeration:
				ename=an.name.split('.')[1]
				ename=ename.replace('/','_')
				ename=ename.replace('-','_')
				if '0' <= ename[0] <= '9':
					ename='_'+ename
				if ename in keyword.kwlist:
					ename+='_'
				simpleprinter << f'''
	{ename} = {"'"+val+"'"}, {an.doc}'''
		
		elif len(self.enumeration)>0:
			simpleprinter << f'''

class {self.annotation.name}(simplebase.SimpleStringSelectionElement):
	{self.annotation.doc}
	_validStrings=("{'","'.join(value for value,annotation in self.enumeration)}",)'''
		elif hasattr(self, 'pattern'):
			if self.annotation.name in ('Integer','NormalizedInstruction','PositiveInteger','PositiveUnlimitedInteger','UnlimitedInteger'):
				base='SimpleIntegerElement'
			elif self.annotation.name in ('Limit','Numerical'):
				base='SimpleNumericalElement'
			#elif self.annotation.name in ('Limit','Numerical'):
			#	base='SimpleFloatElement'
			elif self.annotation.name in ('Boolean',):
				base='SimpleBoolElement'
			else:
				base='SimpleStringElement'
			self._base=base
			simpleprinter << f'''

class {self.annotation.name}(simplebase.{base}):
	{self.annotation.doc}
	_regex=r"{self.pattern}"'''
		elif self._base=="xsd:double":
			simpleprinter << f'''

class {self.annotation.name}(simplebase.SimpleUnlimitedFloatElement):
	{self.annotation.doc}'''			
		else:
			simpleprinter << f'''

class {self.annotation.name}(simplebase.SimpleUnlimitedStringElement):
	{self.annotation.doc}'''
	

class ComplexType:
	typeUsed=False
	types={}
	simple=False
	valArrayType = False
	def __init__(self,complex):
		try:
			self.annotation=Annotation(complex)
			if 'complex type for class' in complex.getprevious().text:
				# workaround for some classes with strange name
				self.annotation.name=re.split('::', complex.getprevious().text)[-1].strip()
		except:
			pass
		if 'name' in complex.attrib:
			self.xmlname=complex.attrib['name']
			self.types['AR:'+self.xmlname]=self
		self.mixed=complex.attrib.get('mixed','false')=='true'
		try:
			if self.annotation.name in ('RuleArguments','SwValues','ValueList','CompuNominatorDenominator'):
				# val array type
				self.valArrayType = True
				# treat val array types as mixed. They are atpMixed according to autosar but not mixed string, and therefore not category mixed according to the xsd
				self.mixed = True
				if self.annotation.name == 'RuleArguments':
					self.simpletype = 'ValTextArray'
				elif self.annotation.name == 'SwValues':
					self.simpletype = 'ValTextMultiDimArray'
				elif self.annotation.name == 'ValueList':
					self.simpletype = 'ValArray'
				elif self.annotation.name == 'CompuNominatorDenominator':
					self.simpletype = 'ValArray'
				else:
					assert False
		except:
			pass
		self.baseclasses=[]
		self.elements=[]
		self.attribGroups=[]
		self.attrib=[]
		for element in complex.iterfind('{http://www.w3.org/2001/XMLSchema}choice/{http://www.w3.org/2001/XMLSchema}element'):
			self.elements.append(Element(element,self))
		simple=complex.find('{http://www.w3.org/2001/XMLSchema}simpleContent')
		if simple is not None:
			self.simple=True
			ext=simple.find('{http://www.w3.org/2001/XMLSchema}extension')
			self.simpletype=ext.attrib['base']
			for attrib in ext.iterfind('{http://www.w3.org/2001/XMLSchema}attribute'):
				self.attrib.append(Attrib(attrib))
			for attribGroup in ext.iterfind('{http://www.w3.org/2001/XMLSchema}attributeGroup'):
				self.attribGroups.append(attribGroup.attrib['ref'])
		for element in itertools.chain(complex.iterfind('{http://www.w3.org/2001/XMLSchema}sequence/{http://www.w3.org/2001/XMLSchema}group'),complex.iterfind('{http://www.w3.org/2001/XMLSchema}choice/{http://www.w3.org/2001/XMLSchema}group')):
			self.baseclasses.append(element.attrib['ref'])
		for attribGroup in complex.iterfind('{http://www.w3.org/2001/XMLSchema}attributeGroup'):
			self.attribGroups.append(attribGroup.attrib['ref'])

	def getbaseclasses(self):
		ret=[]
		for base in self.baseclasses:
			ret.append(base)
			group=Group.types.get(base,None)
			if group is not None:
				ret.extend(group.getbaseclasses())
		return ret
	def getelements(self):
		#ret=sorted(self.elements,key=lambda e:(e.annotation.sequenceOffset if hasattr(e,'annotation') and hasattr(e.annotation,'sequenceOffset') else 0,e.xmlname))
		ret=list(self.elements)
		for base in self.getbaseclasses():
			#ret.extend(sorted(Group.types[base].elements,key=lambda e:(e.annotation.sequenceOffset if hasattr(e,'annotation') and hasattr(e.annotation,'sequenceOffset') else 0,e.xmlname)))
			#ret.extend(sorted(Group.types[base].elements,key=lambda e:e.annotation.sequenceOffset if hasattr(e,'annotation') and hasattr(e.annotation,'sequenceOffset') else 0))
			ret.extend(Group.types[base].get_elements())
		return ret
	def getattribs(self):
		attribs={}
		for group in (AttributeGroup.types[name] for name in self.attribGroups):
			if group.ingroup is None:
				for attrib in group.attribute:
					attribs[attrib.xmlname]=attrib
		for attrib in self.attrib:
			attribs[attrib.xmlname]=attrib
		return list(attribs.values())
	def getattriblist(self):
		attribnames=[]
		for group in (AttributeGroup.types[name] for name in self.attribGroups):
			if group.ingroup is not None:
				for attrib in group.attribute:
					attribnames.append(f'Group.{group.ingroup.annotation.name}.{attrib.annotation.elementname}')
			else:
				for attrib in group.attribute:
					attribnames.append(f'{attrib.annotation.elementname}')
		for attrib in self.attrib:
			attribnames.append(f'{attrib.annotation.elementname}')
		return '('+','.join(attribnames)+')'
	def getaggregationlist(self):
		aggregations=[]
		for base in self.getbaseclasses():
			aggregations.extend(Group.types[base].aggregationslist)
		aggregations.sort(key=lambda e:(e[1].annotation.sequenceOffset,e[0]))
		aggregationnames=[]
		for aggname,agg in aggregations:
			aggregationnames.append(f'Group.{agg.ingroup}.__dict__["{aggname}"]')
		return '('+','.join(aggregationnames)+(')' if len(aggregationnames)!=1 else ',)')
	def complementsimple(self):
		if self.simple and hasattr(self,'annotation') and ('atpObject' not in self.annotation.stereotypes or 'primitive' in self.annotation.stereotypes and not self.annotation.dummyAnnotation):
			simpletype=SimpleType.types[self.simpletype]
			simpletype.annotation=self.annotation
	def generate(self):
		if not self.typeUsed and self.annotation.name!='AUTOSAR':
			return
		baseclasses=['Group.'+Group.types[name].annotation.name for name in self.getbaseclasses().__reversed__()]
		element=None
		if self.simple:
			baseclasses.append("Group.ARObject")
			simpletype=SimpleType.types[self.simpletype]
			reftype=None
			try:
				reftype=SimpleType.types[next(attrib for attrib in self.attrib if attrib.xmlname=='DEST').xmltype]
			except:
				pass
			if len(simpletype.enumeration)>0 and simpletype.enumeration[0][1] is not None:
				baseclasses.extend(('complexbase.EnumTypeBase','complexbase.ContainerBase'))
			elif reftype is not None:
				baseclasses.extend(('complexbase.ModelReference','complexbase.ContainerBase'))
			elif hasattr(simpletype,'_base'):
				if simpletype._base in ('SimpleFloatElement','SimpleUnlimitedFloatElement','xsd:double'):
					baseclasses.extend(('complexbase.FloatTypeBase','complexbase.ContainerBase'))
				elif simpletype._base in ('SimpleNumericalElement',):
					baseclasses.extend(('complexbase.NumericalTypeBase','complexbase.ContainerBase'))
				elif simpletype._base in ('SimpleIntegerElement',):
					baseclasses.extend(('complexbase.IntegerTypeBase','complexbase.ContainerBase'))
				elif simpletype._base in ('SimpleBoolElement',):
					baseclasses.extend(('complexbase.BooleanTypeBase','complexbase.ContainerBase'))
				else:
					baseclasses.extend(('complexbase.StringTypeBase','complexbase.ContainerBase'))
			else:
				baseclasses.extend(('complexbase.StringTypeBase','complexbase.ContainerBase'))
		elif self.mixed:
			if self.annotation.name in ('IntegerValueVariationPoint','PositiveIntegerValueVariationPoint','UnlimitedIntegerValueVariationPoint',):
				baseclasses.extend(('complexbase.IntegerTypeBase','complexbase.MixedStringBase'))
			elif self.annotation.name in ('BooleanValueVariationPoint',):
				baseclasses.extend(('complexbase.BooleanTypeBase','complexbase.MixedStringBase'))
			elif self.annotation.name in ('FloatValueVariationPoint','LimitValueVariationPoint'):
				baseclasses.extend(('complexbase.FloatTypeBase','complexbase.MixedStringBase'))
			elif self.annotation.name in ('NumericalValueVariationPoint',):
				baseclasses.extend(('complexbase.NumericalTypeBase','complexbase.MixedStringBase'))
			elif self.valArrayType:
				baseclasses.extend(('complexbase.ValArrayTypeBase','complexbase.MixedBase'))
			else:
				baseclasses.extend(('complexbase.StringTypeBase','complexbase.MixedStringBase'))
		elif 'atpMixed' in self.annotation.stereotypes:
				baseclasses.append('complexbase.MixedBase')			
		elif self.annotation.name in ('EcucTextualParamValue',):
			baseclasses.extend(('complexbase.EcucValueTypeBase','complexbase.StringTypeBase'))
		elif self.annotation.name in ('EcucNumericalParamValue',):
			baseclasses.extend(('complexbase.EcucValueTypeBase','complexbase.NumericalTypeBase'))
		elif self.annotation.name in ('EcucAddInfoParamValue',):
			baseclasses.extend(('complexbase.EcucValueTypeBase','complexbase.StringTypeBase'))
		elif self.annotation.name in ('EcucInstanceReferenceValue',):
			baseclasses.extend(('complexbase.EcucValueTypeBase','complexbase.ModelReference'))
		elif self.annotation.name in ('EcucReferenceValue',):
			baseclasses.extend(('complexbase.EcucValueTypeBase','complexbase.ModelReference'))
		elif 'instanceRef' in self.annotation.stereotypes:
			baseclasses.append('complexbase.InstanceRefBase')			
		else:
			element=next((element for element in (element for baseclass in self.getbaseclasses() for element in Group.types[baseclass].get_elements()) if 'isOfType' in element.annotation.stereotypes),None)
			if element is not None:
				baseclasses.append('complexbase.PrototypeBase')
			else:
				baseclasses.append('complexbase.ComplexTypeBase')
		printer << f'''

class {self.annotation.name}({','.join(b for b in baseclasses)}):
	{self.annotation.doc}
	_instances=[]'''
		if not self.simple and ('atpMixed' in self.annotation.stereotypes or 'atpMixedString' in self.annotation.stereotypes) or getattr(self,'valArrayType',False):
			# arrayValType may not be atpMixed because it may only contain one subcontainer. But the behaviour is the same since it contain an array that cannot be merged
			printer << f'''
	_atpMixed=True'''
		if self.annotation.name=='AUTOSAR':
			printer << f'''
	def __repr__(self):
		return "autosar"'''
		for attrib in self.getattribs():
			atypename,aname=attrib.annotation.name.split('.')
			aname=aname.replace('/','_')
			if aname in keyword.kwlist:
				aname+='_'
			printer << f'''
	{aname}=complexbase.Attribute("{aname}",SimpleTypes.{SimpleType.types[attrib.xmltype].annotation.name},'{attrib.xmlname}',{attrib.required},{attrib.annotation.doc})'''
		printer << f'''
	_attribs={self.getattriblist()}'''

		if self.simple and reftype is not None:
			printer << f'''
	_dests={{{','.join(('"'+e[0]+'":Group.'+Group.types['AR:'+e[0]].annotation.name for e in reftype.enumeration))}}}'''		
		elif self.simple:
			printer << f'''
	_valueType=SimpleTypes.{SimpleType.types[self.simpletype].annotation.name}'''
		elif self.mixed:
			if self.annotation.name in ('BooleanValueVariationPoint',):
				printer << f'''
	_valueType=SimpleTypes.Boolean'''				
			elif self.annotation.name in ('FloatValueVariationPoint','LimitValueVariationPoint'):
				printer << f'''
	_valueType=SimpleTypes.Float'''				
			elif self.annotation.name in ('NumericalValueVariationPoint',):
				printer << f'''
	_valueType=SimpleTypes.AnyNumerical'''				
			elif self.annotation.name in ('IndexEntry','IntegerValueVariationPoint','PositiveIntegerValueVariationPoint','UnlimitedIntegerValueVariationPoint',):
				printer << f'''
	_valueType=SimpleTypes.Integer'''
			elif self.valArrayType:
				printer << f'''
	_valueType=SimpleTypes.{self.simpletype}'''
			else:
				printer << f'''
	_valueType=SimpleTypes.String'''
		elif element is not None:
			printer << f'''
	_isOfType="_{element.annotation.name.rsplit('.',1)[-1]}_child"'''
		
	
class Group:
	types={}
	classtogroup={}
	def __init__(self,group):
		self.annotation=Annotation(group)
		if 'element group for class' in group.getprevious().text:
			# workaround for some classes with strange name
			self.annotation.name=re.split('::', group.getprevious().text)[-1].strip()
		self.xmlname=group.attrib['name']
		self.types['AR:'+self.xmlname]=self
		self.classtogroup[self.annotation.name]=self
		self._elements=[]
		self.attribGroups=[]
		self.aggregationslist=[]
		self.grouprefs=[]
		self.attrib=[]
		self.initstr=iostream(io.StringIO())
		def addelement(self,xml,unbounded=False,minOccurs=False):
			if xml.attrib.get("name")=="COMPU-CONST":
				print('a')
			for child in xml:
				if child.tag=='{http://www.w3.org/2001/XMLSchema}group':
					self.grouprefs.append((child.attrib['ref'],len(self._elements),unbounded, minOccurs, self.annotation.name))
				elif child.tag in ('{http://www.w3.org/2001/XMLSchema}choice','{http://www.w3.org/2001/XMLSchema}sequence'):
					if 'maxOccurs' in child.attrib and child.attrib['maxOccurs']=='unbounded':
						unbounded=True
					addelement(self,child,unbounded, minOccurs or ('minOccurs' in child.attrib and child.attrib['minOccurs']=='0'))
				elif child.tag=='{http://www.w3.org/2001/XMLSchema}element':
					self._elements.append(Element(child,self))
					if unbounded:
						self._elements[-1].annotation.maxOccurs=0xffffffff
					if minOccurs:
						self._elements[-1].annotation.minOccurs=0
			
		addelement(self,group)
	def get_elements(self):
		import copy
		if self.grouprefs:
			for groupname,idx,unbounded, minOccurs, groupAsrName in reversed(self.grouprefs):
				group = Group.types[groupname]
				elements = group.get_elements()
				for e in reversed(elements):
					class ElementCopy:
						parent = self
						annotation = copy.deepcopy(e.annotation)
						generate=type(e).generate
						get_parent_name = type(e).get_parent_name
						gettype=e.gettype
						xmlname=e.xmlname
					new_e = ElementCopy()
					new_e.annotation.name = groupAsrName + '.' + new_e.annotation.name.split('.')[-1]
					if hasattr(e,'xmltypename'):
						new_e.xmltypename = e.xmltypename
					if hasattr(e,'type'):
						new_e.type = e.type
					if unbounded:
						new_e.annotation.maxOccurs=0xffffffff
					if minOccurs:
						new_e.annotation.minOccurs=0
					self._elements.insert(idx,new_e)
					Element.generate_groupinit(new_e)
		self.grouprefs = None
		return self._elements
	def getbaseclasses(self):
		return []

	def generate(self):
		if self.annotation.name=='EcucDefinitionElement':
			self.initstr << f'''
		self._validationConds=[]
		self._validationCondGrouped=None'''
		elif self.annotation.name=='Referrable':
			self.initstr << f'''
		self._references=[]'''
		groupprinter << f'''

class {self.annotation.name}(complexbase.GroupBase):
	{self.annotation.doc}'''
		for attrib in (attrib for attribGroup in self.attribGroups for attrib in attribGroup.attribute):
			atypename,aname=attrib.annotation.name.split('.')
			aname=aname.replace('/','_')
			if aname in keyword.kwlist:
				aname+='_'
			groupprinter << f'''
	{aname}=complexbase.Attribute("{aname}",SimpleTypes.{SimpleType.types[attrib.xmltype].annotation.name},'{attrib.xmlname}',{attrib.required},{attrib.annotation.doc})'''
		self.get_elements() # make sure that the initstr is updated before used
		if self.initstr._file.getvalue()!='':# and 'atpMixed' not in self.annotation.stereotypes:
			groupprinter << '''
	def __init__(self):
		super().__init__()'''
			groupprinter << self.initstr._file.getvalue()
		for element in self.get_elements():
			element.generate(groupinit,self)

	
for element in tree.getroot():#.iter('{*}group'):
	if element.tag=='{http://www.w3.org/2001/XMLSchema}group':
		Group(element)
	elif element.tag=='{http://www.w3.org/2001/XMLSchema}complexType':
		ComplexType(element)
	elif element.tag=='{http://www.w3.org/2001/XMLSchema}attributeGroup':
		AttributeGroup(element)
	elif element.tag=='{http://www.w3.org/2001/XMLSchema}simpleType':
		SimpleType(element)
	elif element.tag=='{http://www.w3.org/2001/XMLSchema}complexType':
		ComplexType(element)
	else:
		continue

#for xmlname,g in list(Group.types.items()):
#	if xmlname in ComplexType.types:
#		c=ComplexType.types[xmlname]
#		if xmlname in c.baseclasses:
#			c.elements.extend(g.elements)
#			c.baseclasses.remove(xmlname)
#			del Group.types[xmlname]
simpleprinter << '''
from  .. import simplebase

class ValTextArray(simplebase.SimpleArrayElement):
	_multidim = False
	"""Array type with text or numerical content"""

class ValTextMultiDimArray(simplebase.SimpleArrayElement):
	"""Multi dimensional array type with text or numerical content"""

class ValArray(simplebase.SimpleArrayElement):
	_multidim = False
	_text = False
	"""Array type with numerical content"""

class AnyNumerical(simplebase.SimpleNumericalElement):
	"""This primitive specifies a numerical value. It can be denoted in different formats such as Binary, Decimal, Octal, Hexadecimal, Float.

The value can be expressed in octal, hexadecimal, binary representation. Negative numbers can only be expressed in decimal or float notation."""
	_regex=r"(true|TRUE|false|FALSE|0x[0-9a-f]+)|(0[0-7]+)|(0b[0-1]+)|(([+\-]?[1-9][0-9]+(\.[0-9]+)?|[+\-]?[0-9](\.[0-9]+)?)([eE]([+\-]?)[0-9]+)?)|\.0|INF|-INF|NaN"

'''

groupprinter << '''
from ..support import *
from . import SimpleTypes
from .. import complexbase
from collections import defaultdict
'''

printer << f'''
from ..support import *
from . import Group
from . import SimpleTypes
from .. import complexbase
__path__=[]

AR_NS='http://autosar.org/schema/r4.0'
AR={{None : AR_NS}}
schemaLocation='http://autosar.org/schema/r4.0 {schemafile.split('/')[-1]}'
schemafile="{schemafile}"
from collections import defaultdict
'''

for e in Element.elements:
	e.generate_groupinit()
	
for t in ComplexType.types.values():
	if hasattr(t,'complementsimple'):
		t.complementsimple()

for t in SimpleType.types.values():
	try:
		t.generate()
	except:
		raise

for xag,ag in AttributeGroup.types.items():
	if xag in Group.types:
		ag.ingroup=Group.types[xag]
		Group.types[xag].attribGroups.append(ag)

for g in Group.types.values():
	g.generate()

for t in ComplexType.types.values():
	t.generate()

printer << f'''

def _init():
	from . import Group{groupinit._file.getvalue()}
	
'''
for t in ComplexType.types.values():
	if not t.simple and (t.typeUsed or t.annotation.name=='AUTOSAR'):
		elist=[]
		for idx,element in enumerate(t.getelements()):
			a,b = element.childref.rsplit('.',1)
			x,y = a.split(':')
			childref = x + ':(' + y + '.__dict__["' + b + '"],' + str(idx) + ')'
			elist.append(childref)
		#elist.sort(key=lambda e:e[0])
		#elist.sort(key=lambda e:e[1])
		printer << f'''
	{t.annotation.name}._xmlchildren={{{','.join(elist)}}}'''
		printer << f'''
	{t.annotation.name}._aggregations={t.getaggregationlist()}'''

#groupprinter << f'''
#def _init():
#	import autosar_4p0 as Autosar{groupinit._file.getvalue()}
#'''

printer._file.close()
groupprinter._file.close()
simpleprinter._file.close()

import AutoMAT

	